package com.zlsy.thread.deadlock;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Random;

/**
 * @author zhouliang
 * @date 2020/5/13 21:05
 *
 * 解决牛郎织女活锁的示例
 *
 */
public class SolveLivelock {

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    static class Spoon {
        /**
         * 这把勺子归属的人
         */
        private SolveLivelock.Diner owner;

        public synchronized void use() {
            System.out.printf("%s has eating", owner.name);
        }
    }

    static class Diner {
        private String name;
        private boolean isHungry;

        public Diner(String name) {
            this.name = name;
            this.isHungry = true;
        }

        public void eatWith(SolveLivelock.Spoon spoon, SolveLivelock.Diner spouse) {
            while (isHungry) {
                //勺子的拥有人是否是我，如果是我才不进这个if，
                if (spoon.owner != this) {
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    continue;
                }

                //先检查对方饿不饿，对方饿了就把勺子给对方
                Random random = new Random();
                if (spouse.isHungry && random.nextInt(10) < 9) {
                    System.out.println(name + "说：亲爱的" + spouse.name + "你先吃吧！");
                    //勺子给对方
                    spoon.setOwner(spouse);

                    continue;
                }
                //如果我拿到了勺子，对方也不饿，那我就把勺子用了
                spoon.use();
                //不饿了
                isHungry = false;
                System.out.println(name + ":我吃完了！");
                //把勺子给对方
                spoon.setOwner(spouse);
            }
        }
    }


    public static void main(String[] args) {
        SolveLivelock.Diner diner1 = new SolveLivelock.Diner("牛郎");
        SolveLivelock.Diner diner2 = new SolveLivelock.Diner("织女");

        SolveLivelock.Spoon spoon = new SolveLivelock.Spoon(diner1);

        new Thread(new Runnable() {
            @Override
            public void run() {
                diner1.eatWith(spoon, diner2);
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                diner2.eatWith(spoon, diner1);
            }
        }).start();
    }
}
